#pragma once

namespace Ut
{

template <typename T, int BlockSize = 4096>
class Pool
{
public:
	Pool() : m_pHead(0), m_allocated(0) {}

	~Pool()
	{
		for (uint8* p : m_chunks)
			delete [] p;

		Assert(m_allocated == 0);
	}

	template <typename... Args>
	T* Allocate(Args&&... args)
	{
		if (m_pHead != 0)
		{
			void* p = m_pHead;
			m_pHead = m_pHead->pNext;
			m_allocated++;
			new (p) T(args...);
			return (T*) p;
		}

		AddChunk();
		return Allocate(args...);
	}

	void Free(T* p)
	{
		p->~T();
		Item* pHead = (Item*) p;
		pHead->pNext = m_pHead;
		m_pHead = pHead;
		m_allocated--;
	}

	int Allocated() const
	{
		return m_allocated;
	}

private:
	void AddChunk()
	{
		m_chunks.emplace_back(new uint8[BlockSize]);
		int n = BlockSize / sizeof(T);
		uint8* p = m_chunks.back();

		for (int i = 0; i < n; i++)
		{
			Item* pHead = (Item*) p;
			pHead->pNext = m_pHead;
			m_pHead = pHead;
			p += sizeof(T);
		}
	}

	int m_allocated;

	struct Item
	{
		Item* pNext;
	};

	Item* m_pHead;

	std::vector<uint8*> m_chunks;
};

}